Skip to content

feat: replay scrollback history to boo ui views on attach#72

Closed
BenLocal wants to merge 1 commit into
coder:mainfrom
BenLocal:feat/ui-scrollback-replay
Closed

feat: replay scrollback history to boo ui views on attach#72
BenLocal wants to merge 1 commit into
coder:mainfrom
BenLocal:feat/ui-scrollback-replay

Conversation

@BenLocal

Copy link
Copy Markdown

A boo ui view's scrollback lived only in its local terminal and was built from output streamed while attached. Switching sessions destroys the view (and its scrollback) and creates a fresh one, and the daemon's attach replay (repaint) deliberately sends only the visible screen, so a just-switched-to view had nothing above the current screen to scroll up into.

Replay the window's history to ui views on attach:

  • window.historyReplay: VT bytes that reproduce the scrollback HISTORY (rows above the visible screen) as a styled, scrolling stream, plus a full-screen flush so every history row lands in the canvas's scrollback before the repaint's erase, with no blank gap. Fed before repaint(), a fresh terminal ends up holding the same scrollback, viewport at bottom.
  • protocol.AttachPayload: the attach handshake now carries a ui flag (decodes a bare 4-byte size payload as non-ui for slack).
  • The daemon sends the history (chunked across output frames, since it can exceed max_payload) before the repaint, but only to ui clients. A plain boo attach is raw passthrough, where a history dump would spam the user's real terminal, so it stays history-free.

Verified end to end: a ui attach receives scrolled-off history; a plain attach receives only the visible screen.

A boo ui view's scrollback lived only in its local terminal and was
built from output streamed while attached. Switching sessions destroys
the view (and its scrollback) and creates a fresh one, and the daemon's
attach replay (repaint) deliberately sends only the visible screen, so a
just-switched-to view had nothing above the current screen to scroll up
into.

Replay the window's history to ui views on attach:

- window.historyReplay: VT bytes that reproduce the scrollback HISTORY
  (rows above the visible screen) as a styled, scrolling stream, plus a
  full-screen flush so every history row lands in the canvas's scrollback
  before the repaint's erase, with no blank gap. Fed before repaint(), a
  fresh terminal ends up holding the same scrollback, viewport at bottom.
- protocol.AttachPayload: the attach handshake now carries a `ui` flag
  (decodes a bare 4-byte size payload as non-ui for slack).
- The daemon sends the history (chunked across output frames, since it
  can exceed max_payload) before the repaint, but only to ui clients.
  A plain `boo attach` is raw passthrough, where a history dump would
  spam the user's real terminal, so it stays history-free.

Verified end to end: a ui attach receives scrolled-off history; a plain
attach receives only the visible screen.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@kylecarbs kylecarbs left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this, @BenLocal. I reviewed it and built/tested it end to end with Zig 0.15.2. The feature itself is well built and correct: the history reconstruction (serialize scrollback history as styled VT, reset SGR, scroll a full screen so every history row lands in the canvas scrollback before the repaint's erase, then repaint the visible screen) is sound and unit-tested, plain attach correctly stays history-free, and alt-screen sessions correctly yield no history.

Verification: zig fmt --check, zig build, zig build test (120), zig build test-integration (69), and zig build test-all -Doptimize=ReleaseSafe all pass.

Two things to address before merge:

  1. Upgrade incompatibility. Widening attach to a 5-byte AttachPayload means a daemon started by a pre-upgrade binary (which decodes attach as a strict 4-byte SizePayload) rejects the new payload. After upgrading, a new boo ui/boo attach can't attach to a pre-upgrade session, the client reports lost connection. I confirmed this against a main daemon. Suggested fix: keep attach as the 4-byte SizePayload and send a separate ui marker message before attaching; older daemons ignore the unknown message type and just attach with no history (graceful degradation).

  2. No integration test for the new path. The window-level unit test is great, but the daemon -> client replay and the plain-attach gating aren't covered by the PTY suite.

Rather than block on a round-trip, I pushed a follow-up that implements both on top of your commit (yours is preserved underneath): #75. Happy to fold it back in here or land it there, whichever you prefer.

Disclosure: this review was generated by Coder Agents on behalf of @kylecarbs.

kylecarbs added a commit that referenced this pull request Jun 17, 2026
Replays a session's scrollback history to boo ui views on attach, so a wheel-up can page output that scrolled off before the view attached (e.g. after switching sessions and back, or reattaching). Builds on #72 by @BenLocal with:

- a backward-compatible `ui` marker message so older daemons attach with no history instead of rejecting the client,
- a blank-hold until the attach repaint completes so a large replay never flashes partial scrollback, revealed incrementally so the post-attach write can't wedge an undrained ui on macOS,
- PTY integration coverage for both the ui replay and the plain-attach (history-free) gating.

Plain `boo attach` is unchanged: it stays raw passthrough and receives only the visible screen.

Co-authored-by: benshi <807629978@qq.com>

Generated by Coder Agents on behalf of @kylecarbs.
@kylecarbs kylecarbs mentioned this pull request Jun 17, 2026

Copy link
Copy Markdown
Member

Thanks @BenLocal — this shipped in v0.5.22 🎉

We landed it via #75, which builds directly on your commit (preserved with you as a co-author) and adds a few things on top: a backward-compatible ui marker so an already-running older daemon degrades gracefully instead of rejecting the attach, a blank-hold until the attach repaint completes so a large history replay never flashes partial scrollback, and PTY integration tests covering both the boo ui replay and the plain-attach (history-free) gating.

Closing this in favor of #75. Really appreciate the contribution!

Generated by Coder Agents on behalf of @kylecarbs.

@kylecarbs kylecarbs closed this Jun 17, 2026
@kylecarbs

Copy link
Copy Markdown
Member

Sorry for the pure LLM message above. The content above is accurate, and is fixed in the latest release.

Thanks for opening this!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants